
from fcf import *
from ks import *
from cStringIO import StringIO

branch = [[None, u'\u30d7\u30ed\u30ed\u30fc\u30b01\u65e5\u76ee', u'\u30d7\u30ed\u30ed\u30fc\u30b02\u65e5\u76ee', u'\u30d7\u30ed\u30ed\u30fc\u30b03\u65e5\u76ee'],
          u'\u30bb\u30a4\u30d0\u30fc\u30eb\u30fc\u30c8', u'\u51db\u30eb\u30fc\u30c8', u'\u685c\u30eb\u30fc\u30c8']
days = [None,
		u'\u4e00\u65e5\u76ee',
		u'\u4e8c\u65e5\u76ee',
		u'\u4e09\u65e5\u76ee',
		u'\u56db\u65e5\u76ee',
		u'\u4e94\u65e5\u76ee',
		u'\u516d\u65e5\u76ee',
		u'\u4e03\u65e5\u76ee',
		u'\u516b\u65e5\u76ee',
		u'\u4e5d\u65e5\u76ee',
		u'\u5341\u65e5\u76ee',
		u'\u5341\u4e00\u65e5\u76ee',
		u'\u5341\u4e8c\u65e5\u76ee',
		u'\u5341\u4e09\u65e5\u76ee',
		u'\u5341\u56db\u65e5\u76ee',
		u'\u5341\u4e94\u65e5\u76ee']

days_to_n = dict(zip(days[1:], range(1, len(days))))

class Event:
	def __init__(self, event_type, **params):
		self.type = event_type
		self.params = params

	def __str__(self):
		if self.type == 'text':
			return '<rule.event:%s, text:%s>' % (self.type, self['text'].encode(sys.getfilesystemencoding()))
		return '<rule.event:%s>' % self.type

	def __getitem__(self, key):
		return self.params[key]

class Rule:
	def __init__(self, resource, step = None):
		self.resource = resource
		self.state = {}
		self.step = step or [0, 1, 0, 0, 0] # [branch, day, scene, page, line]
		self.load()

		self.want = None

	def log(self, text):
		print '[log]', text.replace(u'\u30fb', u'\xb7')

	def load(self): # TODO: this re-load the .fcf, avoid this
		#self.log('loading ' + self.target_rule(self.step).name)
		self.fcf = read_fcf(self.target_rule(self.step).decode())
		self.ks = read_ks((self.target_script(self.step) or '').decode())

	def target_script(self, target):
		if target[0] == 0:
			file = u'%s.ks' % branch[target[0]][target[1]]
		else:
			file = u'%s%s-%02d.ks' % (branch[target[0]], days[target[1]], target[2])
		return self.resource[file]

	def target_rule(self, target):
		if target[0] == 0:
			return '''xxx\n0;SCENE'0'-1'0'0'0'0'16777215'0'0''1'0:1:1:0'0\n1;OUTERLABEL'1'-71'581'77'641'-126'16777215'0'0''0'\n'''
		file = u'%s%s.fcf' % (branch[target[0]], days[target[1]])
		return self.resource[file]

	def next(self, input=None):
		if self.want and not input:
			return
		if self.want == 'selecter' and not isinstance(input, int):
			return
		want = self.want
		self.want = None

		script, pages, lines = self.ks
		if want == 'selecter':
			return self.select(input)
		elif self.step[-1] >= len(script):
			event = self.end_scene()
			if event:
				return event
			else:
				return Event('scene') # XXX: FIXME: not good, not clear
		else:
			x = script[self.step[-1]]
			self.step[-1] += 1
			if isinstance(x, basestring):
				if '[l]' in x or '[r]' in x:
					self.want = 'click'
				elif not isinstance(script[self.step[-1]], basestring): # FIXME: out of bound?
					if '[ws canskip=true]' not in x:
						self.want = 'click'
				elif '[ws canskip=true]' in script[self.step[-1]]: # FIXME: out of bound?
					self.want = 'click'
				return Event('text', text=x)
			elif isinstance(x, int):
				return Event('page', page=x)
			elif isinstance(x, list):
				if x == ['l', {}]:
					self.want = 'click'
				return Event(x[0], **x[1])
			else:
				assert False

	def skip_scene(self):
		self.step[-1] = len(self.ks[0])

	def select(self, selection):
		if type(selection) != int:
			return
		n = self.step[2]
		rule = self.fcf
		assert n in rule
		assert rule[n]['type'] == 'SELECTER'
		alternative = rule[n]['alternative']
		assert len(alternative) > 1
		assert selection
		assert type(selection) == int
		self.goto_scene(alternative[selection-1]['target'])
		return Event('scene')

	def end_scene(self):
		n = self.step[2]
		rule = self.fcf
		if n in rule:
			if rule[n]['type'] == 'SELECTER':
				alternative = rule[n]['alternative']
				if len(alternative) > 1:
					self.step[-1] = -1
					self.want = 'selecter'
					return Event('selecter', alternative=[x['text'] for x in alternative])
				elif len(alternative) == 1:
					raise NotImplementedError()
				else:
					raise NotImplementedError()
			elif rule[n]['type'] == 'SCENE':
				scene = rule[n]
				jump = self.decide_jump(scene['jump'])
				target = jump['target']
				n = target
				self.goto_scene(n)
			else:
				raise NotImplementedError()
		else:
			raise NotImplementedError()

	def goto_scene(self, n):
		scene = self.fcf[n]
		if scene['type'] == 'SCENE':
			self.log('[%d] %s' % (n, scene['title']))
			for op in scene['flagoperation']:
				self.do_op(op)
			self.step[-3:] = [n, 0, 0]
			self.load()
		elif scene['type'] == 'SELECTER':
			self.log('[%d] %s' % (n, scene['title']))
			self.step[-3:] = [n, 0, 0]
			self.load()
		elif scene['type'] == 'OUTERLABEL':
			self.log('[%d] OUTERLABEL' % n)
			self.log('********** ' + scene['file'] + ' **********')
			self.step[1] += 1 # day++
			self.step[2:] = [scene['target'], 0, 0]
			if self.step[0] == 0 and self.step[1] > 3:
				self.step = [1, 1, 0, 0, 0] # end of prologue
			self.load()
			self.goto_scene(scene['target'])
		else:
			raise NotImplementedError()

	def decide_jump(self, jump):
		if len(jump) == 1:
			return jump[0]
		elif len(jump) > 1:
			for s in jump:
				if {'and': all, 'or': any}[s['condition_op']](map(self.right, s['condition'])):
					return s
			raise RuntimeError('No condition matched, wired!')
		else:
			raise RuntimeError('Game over!')

	def right(self, condition):
		var, op, value = condition
		var_value = self.state.get(var, 0) # XXX: should I set init to 0?
		if op == '==':
			return var_value == value
		elif op == '!=':
			return var_value != value
		elif op == '<':
			return var_value < value
		elif op == '>':
			return var_value > valuea
		elif op == '<=':
			return var_value <= value
		elif op == '>=':
			return var_value >= value
		else:
			assert False, 'operator %s is not supported' % op

	def do_op(self, operation):
		var, op, value = operation
		var_value = self.state.get(var, 0) # XXX: should I set init to 0?
		if op == '=':
			self.state[var] = value
		elif op == '+':
			self.state[var] = var_value + value
		elif op == '-':
			self.state[var] = var_value - value
		else:
			assert False, 'operator %s is not supported' % op
		self.log('%s %s %d' % (var, op, value))


	def console_loop(self):
		input = 'click'
		while True:
			event = self.next(input)
			assert event
			if event.type == 'selecter':
				alternative = event['alternative']
				assert len(alternative) > 1
				for i in range(len(alternative)):
					print i+1, alternative[i]
				i = raw_input('[?] ')
				input = int(i)
			elif event.type == 'text':
				print event['text']
			else:
				input = 'click'

if __name__ == '__main__':
	from resource import Resource
	rule = Rule(Resource())
	rule.console_loop()

